﻿/*
 * iec104app.cpp
 *
 *  Created on: 2017年2月4日
 *      Author: work
 */

#include <dm/export.hpp>

#define DM_API_PROTOCOL DM_API_EXPORT

#include <dm/protocol/frame104apdu.hpp>
#include <dm/bytes.hpp>

namespace dm{
namespace protocol{

CFrame104Apdu::CFrame104Apdu( dm::uint8* buf,const dm::uint8& size ):
		m_data(buf),m_size(size),m_len(0),m_maxLenOfApdu(253){
	m_LenOfCommonAddr = 1;
	m_LenOfCause = 1;
	m_LenOfObjAddr = 2;

	m_commonLen = getCommonLen();
}

CFrame104Apdu& CFrame104Apdu::operator =( const CFrame104Apdu& apdu ){
	m_LenOfCommonAddr = apdu.m_LenOfCommonAddr;
	m_LenOfCause = apdu.m_LenOfCause;
	m_LenOfObjAddr = apdu.m_LenOfObjAddr;
	m_commonLen = apdu.m_commonLen;
	m_len = apdu.m_len;
	setData( apdu.m_data,apdu.m_len );

	return *this;
}

CFrame104Apdu::comm_addr_t CFrame104Apdu::getCommonAddress()const{
	if( m_LenOfCommonAddr==1 )
		return getCommonAddressArray()[0];
	else{
		comm_addr_t rt = getCommonAddressArray()[1];
		rt <<= 8;
		rt += getCommonAddressArray()[0];
		return rt;
	}
}

void CFrame104Apdu::setCommonAddress( const comm_addr_t& addr ){
	m_data[2 + m_LenOfCause] = dm::uint8(addr);
	if( m_LenOfCommonAddr==2 )
		m_data[3 + m_LenOfCause] = (addr>>8);
}

bool CFrame104Apdu::checkCommonAddress( const dm::uint16& address )const{
	if( getCommonAddressArray()[0]!=dm::lowByte_u16(address) )
		return false;
	if( m_LenOfCommonAddr==2 && getCommonAddressArray()[1]!=higByte_u16(address) )
		return false;
	return true;
}

/**
 * 从LDU传过来的数据长度
 * 数据长度可以是0，表示U帧或者S帧
 * 否则长度应该大于公共数据长度
 * 这里并没有对数据的内容进行校验
 */
bool CFrame104Apdu::checkData( const dm::uint8& len ){
	if( len>0 && len<m_commonLen )
		return false;
	m_len = len;
	return true;
}

bool CFrame104Apdu::setData( const dm::uint8* buf,const dm::uint8& len,const dm::uint8& p ){
	if( len+p>m_len )
		return false;
	for( dm::uint8 i=p;i<len+p;++i )
		m_data[i] = buf[i];
	return true;
}

const dm::uint8* CFrame104Apdu::getData( const dm::uint8& p )const{
	if( p>=m_len )
		return 0;
	else
		return m_data + p;
}

dm::uint8* CFrame104Apdu::getData( const dm::uint8& p ){
	if( p>=m_len )
		return 0;
	else
		return m_data + p;
}

/**********************************************
 * 应用数据处理
 **********************************************/

/**
 * 测试信息结构
 * @param lenOfObj 单个信息长度,不含地址
 * @return
 */
bool CFrame104Apdu::checkInfo( const dm::uint8& lenOfObj )const{
	if( getElementsCount()==0 )
		return false;

	dm::uint8 objLen = lenOfObj;
	if( isGroupObjs() ){
		objLen *= getElementsCount();
		objLen += m_LenOfObjAddr;
	}else{
		objLen += m_LenOfObjAddr;
		objLen *= getElementsCount();
	}

	return m_len==(objLen+getCommonLen());
}

/**
 * 获取信息地址
 * @param lenOfObj 单个信息长度，不含信息地址
 * @param obj
 * @return
 */
dm::uint32 CFrame104Apdu::getInfoAddr( const dm::uint8& lenOfObj,const int& obj )const{
	if( isGroupObjs() )	// 组地址
		return getObjAddress( m_data + getCommonLen()) + obj;
	else
		return getObjAddress( m_data + getCommonLen() + obj*(lenOfObj+m_LenOfObjAddr) );
}

/**
 * 获取信息缓冲区
 * @param lenOfObj
 * @param obj
 * @return
 */
const dm::uint8* CFrame104Apdu::getInfo( const dm::uint8& lenOfObj,const int& obj )const{
	if( isGroupObjs() )
		return m_data + getCommonLen() + m_LenOfObjAddr + lenOfObj * obj;
	else
		return m_data + getCommonLen() + (m_LenOfObjAddr + lenOfObj ) * obj + m_LenOfObjAddr;
}

void CFrame104Apdu::makeInfo( const dm::uint8& lenOfObj,const bool& group,const dm::uint8& elemCount ){
	setGroupObjs(group);
	setElementsCount(elemCount);
	if( group )
		m_len = getCommonLen() + m_LenOfObjAddr + lenOfObj * elemCount;
	else
		m_len = getCommonLen() + ( m_LenOfObjAddr + lenOfObj ) * elemCount;
}

void CFrame104Apdu::setInfoAddr( const dm::uint8& lenOfObj,const dm::uint32& objAddr,const int& obj ){
	if( isGroupObjs() )
		setObjAddress(objAddr,m_data+getCommonLen());
	else
		setObjAddress(objAddr,m_data+getCommonLen()+obj*(lenOfObj+m_LenOfObjAddr));
}

dm::uint8* CFrame104Apdu::getInfo( const dm::uint8& lenOfObj,const int& obj ){
	if( isGroupObjs() )
		return m_data + getCommonLen() + m_LenOfObjAddr + lenOfObj * obj;
	else
		return m_data + getCommonLen() + (m_LenOfObjAddr + lenOfObj ) * obj + m_LenOfObjAddr;
}

dm::uint32 CFrame104Apdu::getObjAddress( const dm::uint8* buf )const{
	dm::uint32 addr = 0;
	for( dm::uint8 i=m_LenOfObjAddr;i>0u;--i ){
		addr <<= 8;
		addr += buf[i-1];
	}

	return addr;
}

void CFrame104Apdu::setObjAddress( dm::uint32 address,dm::uint8* buf )const{
	for( dm::uint8 i=0;i<m_LenOfObjAddr;++i ){
		buf[i] = address;
		address >>= 8;
	}
}

}
}
